package top.chenyaohua.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import org.objectweb.asm.Type;
import top.chenyaohua.annotion.From;
import top.chenyaohua.annotion.RequestBody;
import top.chenyaohua.annotion.ResponseBody;
import top.chenyaohua.asm.AnnotationMetadata;
import top.chenyaohua.asm.FieldMetadata;
import top.chenyaohua.asm.MethodMetadata;
import top.chenyaohua.beans.RequestMapper;
import top.chenyaohua.context.BeanFactory;
import top.chenyaohua.context.RequestMapperHandler;
import top.chenyaohua.utils.AnnotationUtils;
import top.chenyaohua.utils.PropertieUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.*;

public class RequestHandler implements Runnable {

    private Socket socket;

    public RequestHandler (Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run () {
        OutputStream outputStream = null;
        InputStream inputStream = null;
        HttpResponse httpResponse = null;
        HttpRequest request = null;
        try {
            inputStream = socket.getInputStream();
            outputStream = new RequestOutputStream(socket.getOutputStream());

            httpResponse = new HttpResponse(outputStream,inputStream,"HTTP","1.1");
            request = new HttpRequest(outputStream,inputStream);

            //将httpResponse设置到流中
            ((RequestOutputStream) outputStream).setHttpResponse(httpResponse);


            RequestContextHandler.setHttpRequest(request);
            ResponseContextHandler.setHttpResponse(httpResponse);

            String url = request.getUrl();

            if (url == null) {
                return;
            }

            int index = url.indexOf("?");
            RequestMapper rp = RequestMapperHandler.getRequestMapperByUrl(index>0?url.substring(0, index):url);

            if( rp != null  ){

                MethodMetadata method = rp.getMethodMetadata();
                Properties properties = PropertieUtils.getProperties("annotation.properties");
                if ( isConstain(method.getAnnotations(),request.getMethod(),properties.getProperty("request.method")) ){
                    Class<?> cls = rp.getCls();

                    Map<Integer, FieldMetadata> params = method.getParams();
                    Object[] param = new Object[params.size()];
                    Class[] paramCls = rp.getParamCls();
                    //请求格式
                    String contentType = request.getHeader().get("Content-Type");

                    for (int i = 0; i < params.size(); i++) {

                        FieldMetadata fieldMetadata = params.get(i);
                        String name = fieldMetadata.getName();
                        List<AnnotationMetadata> annotations = fieldMetadata.getAnnotations();

                        if ( AnnotationUtils.isExist(annotations, From.class.getName())) {

                            String s = request.getParam(name);
                            param[i] = TypeUtils.cast(s, paramCls[i], ParserConfig.getGlobalInstance());

                        }else if ("application/json".equals(contentType) && AnnotationUtils.isExist(annotations, RequestBody.class.getName())){
                            //读取主体内容
                            InputStream input = request.getInputStream();
                            String s = request.getHeader().get("Content-Length");
                            byte [] bs = new byte[Integer.valueOf(s)];
                            input.read(bs);
                            String sbf = new String(bs);

                            if (fieldMetadata.getType().startsWith("[")) {
                                //数组
                                param[i] = JSONArray.parseArray(sbf, paramCls[i]);

                            }else if(fieldMetadata.getType().startsWith("L")){
                                //对象
                                param[i] = JSONObject.parseObject(sbf, paramCls[i]);
                            }else {
                                //基本数据类型
                                param[i] = TypeUtils.cast(sbf, paramCls[i], ParserConfig.getGlobalInstance());
                            }

                        }

                    }

                    Object object = BeanFactory.getObject(cls.getName());
                    Method mh = cls.getMethod(method.getName(), paramCls);
                    Object invoke = mh.invoke(object, param);

                    httpResponse.getHeader().put("Date",new Date().toString());
                    httpResponse.setStatus("200");
                    httpResponse.setStatusDescribe("OK");

                    if (AnnotationUtils.isExist(method.getAnnotations(), ResponseBody.class.getName())) {
                        //如果添加ResponseBody 则采用json返回数据
                        byte[] bytes = JSON.toJSONString(invoke).getBytes();
                        httpResponse.getHeader().put("Content-Length",String.valueOf(bytes.length));
                        httpResponse.getHeader().put("Content-Type","application/json; charset=UTF-8");

                        outputStream.write(bytes);
                    }else{
                        //如果没有ResponseBody 则返回值视作视图路径
                        write(httpResponse,outputStream,invoke.toString());
                    }
                }else{
                    //请求方式不匹配

                }

            }else if( rp == null){
                //如果找不到映射接口 则查看静态资源
                httpResponse.getHeader().put("Date",new Date().toString());
                httpResponse.setStatus("200");
                httpResponse.setStatusDescribe("OK");

                write(httpResponse,outputStream,url.substring(1));
            }

        } catch (IOException e) {
            e.printStackTrace();
            httpResponse.setStatus("500");
            httpResponse.setStatusDescribe("系统内部错误");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            httpResponse.setStatus("500");
            httpResponse.setStatusDescribe("系统内部错误");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            httpResponse.setStatus("500");
            httpResponse.setStatusDescribe("系统内部错误");
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            httpResponse.setStatus("500");
            httpResponse.setStatusDescribe("系统内部错误");
        }finally {
            try {
                //在此处将HttpResponse设置为null的原因是 outputStream和HttpResponse对象互相依赖
                // 在GC分析时会造成内存无法回收
                ((RequestOutputStream) outputStream).setHttpResponse(null);

                if(outputStream!=null ){
                    outputStream.close();
                }
                if(inputStream!=null ){
                    inputStream.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
            RequestContextHandler.removeHttpRequest();
            ResponseContextHandler.removeHttpResponse();
        }
    }

    private boolean isConstain (List<AnnotationMetadata> annotations, HttpMethod method, String property) {
        List<String> methods = Arrays.asList(property.split(","));

        int i = 1;
        for (AnnotationMetadata itm : annotations) {
            String className = Type.getType(itm.getAnnotationType()).getClassName();
            if (methods.contains(className) ) {
                i++;
                if(className.equalsIgnoreCase("top.chenyaohua.annotion."+method)){
                    //如果有方法注解且和请求方法一样返回true
                   return true;
                }
            }

        }

        //如果没有方法注解返回true 如果有方法注解返回false;
        return i>1?false:true;
    }

    private void write(HttpResponse httpResponse, OutputStream outputStream, String path) throws IOException {
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);

        if(resourceAsStream == null){

            //resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("404.html");
            httpResponse.getHeader().put("Content-Length",String.valueOf(0));
            httpResponse.getHeader().put("Content-Type","text/html;charset=UTF-8");
            httpResponse.setStatus("404");
            outputStream.flush();
            return;
        }
        int available = resourceAsStream.available();
        httpResponse.getHeader().put("Content-Length",String.valueOf(available));
        httpResponse.getHeader().put("Content-Type","text/html;charset=UTF-8");

        byte[] bf = new byte[available];
        resourceAsStream.read(bf);
        resourceAsStream.close();

        outputStream.write(bf);
        outputStream.flush();
    }


}
